home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Utilities Experience
/
The Utilities Experience - Volume 1.iso
/
software
/
emulation
/
frodo
/
src
/
1541fs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-29
|
12KB
|
548 lines
/*
* 1541fs.c - 1541-Emulation im Amiga-Dateisystem
*
* Copyright (C) 1994-1996 by Christian Bauer
*/
/*
* Anmerkungen:
* ------------
*
* Routinen:
* - Die Schnittstelle zu den IEC-Routinen besteht in den Routinen
* FS_Init, FS_Exit, FS_Open, FS_Close, FS_Read und FS_Write:
* FS_Init bereitet die Emulation vor
* FS_Exit beendet die Emulation
* FS_Open öffnet einen Kanal
* FS_Close schließt einen Kanal
* FS_Read liest aus einem Kanal
* FS_Write schreibt in einen Kanal
*
* DriveData:
* - lock enthält den Lock des Verzeichnisses, in dem die Emulation
* ablaufen soll
*
* Directory-Emulation:
* - Wird das Directory geöffnet (Dateiname "$"), wird in T: eine
* temporäre Datei angelegt, die vom Aufbau genau einem 1541-Directory
* entspricht und diese Datei geöffnet. Sie kann dann mit den ganz
* normalen Lesebefehlen verarbeitet werden.
*
* Inkompatibilitäten/Verbesserungen:
* - Keine Wildcards beim Directory-Laden
* - Kein "rohes" Directory-Lesen
* - Keine relativen Dateien
* - Nur 'I'- und 'UJ'-Befehle implementiert
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <string.h>
#include "IEC.h"
#include "1541fs.h"
#include "Display.h"
#define CATCOMP_NUMBERS 1
#include "LocStrings.h"
// Aus Main.asm
extern void ResetC64(void);
// Prototypes
int open_file(DriveData *drive, int channel, const char *filename);
void convert_filename(const char *filename, char *plainname, int *filemode, int *filetype, int *wildflag);
void find_first_file(char *name);
int open_directory(DriveData *drive, int channel, const char *filename);
void close_all_channels(DriveData *drive);
void execute_command(DriveData *drive, const char *command);
char conv_from_64(char c);
char conv_to_64(char c);
/**
** Emulation vorbereiten, Lock auf Verzeichnis holen, prefs zeigt auf den
** Preferences-String
**/
void FS_Init(DriveData *drive, char *prefs)
{
int i;
if (drive->lock = Lock(prefs,ACCESS_READ)) {
for (i=0; i<16; i++) drive->handle[i] = NULL;
drive->cmd_length = 0;
SetError(drive, ERR_STARTUP);
}
}
/**
** Emulation beenden, Lock auf Verzeichnis freigeben
**/
void FS_Exit(DriveData *drive)
{
if (drive->lock) {
close_all_channels(drive);
UnLock(drive->lock);
drive->lock = NULL;
}
}
/**
** Kanal öffnen, filename ist Null-terminiert
**/
int FS_Open(DriveData *drive, int channel, char *filename)
{
SetError(drive, ERR_OK);
// Kanal 15: Dateiname als Befehl ausführen
if (channel == 15) {
execute_command(drive, filename);
return ST_OK;
}
// Vorige Datei schließen, wenn noch offen
if (drive->handle[channel]) {
Close(drive->handle[channel]);
drive->handle[channel] = NULL;
}
if (filename[0] == '$')
return open_directory(drive, channel, filename);
if (filename[0] == '#') {
SetError(drive, ERR_NOCHANNEL);
return ST_OK;
}
return open_file(drive, channel, filename);
}
/*
* Datei wird geöffnet
*/
// Zugriffsmodi
enum {
FMODE_READ, FMODE_WRITE, FMODE_APPEND
};
// Dateitypen
enum {
FTYPE_PRG, FTYPE_SEQ
};
int open_file(DriveData *drive, int channel, const char *filename)
{
BPTR olddir;
BPTR fh = NULL;
char plainname[256];
int filemode = FMODE_READ;
int filetype = FTYPE_PRG;
int wildflag = 0;
olddir = CurrentDir(drive->lock);
convert_filename(filename, plainname, &filemode, &filetype, &wildflag);
// Bei Kanal 0 immer PRG lesen, bei Kanal 1 immer PRG schreiben
if (!channel) {
filemode = FMODE_READ;
filetype = FTYPE_PRG;
}
if (channel == 1) {
filemode = FMODE_WRITE;
filetype = FTYPE_PRG;
}
// Wildcards sind nur beim Lesen erlaubt
if (wildflag) {
if (filemode != FMODE_READ) {
SetError(drive, ERR_SYNTAX33);
CurrentDir(olddir);
return ST_OK;
}
find_first_file(plainname);
}
if (fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE)) {
switch (filemode) {
// Erstes Zeichen lesen, wenn zum Lesen geöffnet
case FMODE_READ:
drive->read_char = FGetC(fh);
break;
// Neue Datei anlegen: E-Bit bei sequentieller Datei löschen
case FMODE_WRITE:
if (filetype == FTYPE_SEQ) {
Close(fh);
SetProtection(plainname, FIBF_EXECUTE);
fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
} else {
Close(fh);
SetProtection(plainname, 0);
fh = Open(plainname, filemode == FMODE_READ ? MODE_OLDFILE : MODE_NEWFILE);
}
break;
// Anhängen: Ans Ende der Datei gehen
case FMODE_APPEND:
Seek(fh, 0, OFFSET_END);
break;
}
}
if (!(drive->handle[channel] = fh))
SetError(drive, ERR_FILENOTFOUND);
CurrentDir(olddir);
return ST_OK;
}
/*
* Dateibezeichnung analysieren, Dateimodus und -typ ermitteln
*/
void convert_filename(const char *srcname, char *destname, int *filemode, int *filetype, int *wildflag)
{
char *p, *q;
int i;
// Nach ':' suchen, p zeigt auf erstes Zeichen hinter dem ':'
if (p = strchr(srcname, ':'))
p++;
else
p = srcname;
// Zeichensatz des Reststrings wandeln -> destname
q = destname;
for (i=0; i<NAMEBUF_LENGTH && (*q++ = conv_from_64(*p++)); i++);
// Nach Modusparametern, getrennt durch ',' suchen
p = destname;
while (p = strchr(p, ',')) {
// String hinter dem ersten ',' abschneiden
*p++ = 0;
switch (*p) {
case 'p':
*filetype = FTYPE_PRG;
break;
case 's':
*filetype = FTYPE_SEQ;
break;
case 'r':
*filemode = FMODE_READ;
break;
case 'w':
*filemode = FMODE_WRITE;
break;
case 'a':
*filemode = FMODE_APPEND;
break;
}
}
// Nach Wildcards suchen, '*' durch '#?' ersetzen und alles danach abschneiden
*wildflag = (strchr(destname, '?') != NULL);
if (p = strchr(destname, '*')) {
*p++ = '#';
*p++ = '?';
*p++ = 0;
*wildflag = TRUE;
}
}
/*
* Erste zum Muster passende Datei suchen den Dateinamen ermitteln
*/
void find_first_file(char *name)
{
struct AnchorPath *a;
if (a = AllocMem(sizeof(struct AnchorPath), MEMF_CLEAR|MEMF_PUBLIC)) {
if (!MatchFirst(name, a)) {
strncpy(name, a->ap_Info.fib_FileName, NAMEBUF_LENGTH);
}
MatchEnd(a);
FreeMem(a, sizeof(struct AnchorPath));
}
}
/*
* Directory wird geöffnet, temporäre Datei erstellen
*/
struct FileInfoBlock fib;
int open_directory(DriveData *drive, int channel, const char *filename)
{
BPTR fh;
char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A";
char *p, *q;
int i;
if (!Examine(drive->lock, &fib)) return ST_OK;
if (!(fh = Open("T:Frodo$File", MODE_NEWFILE))) return ST_OK;
// Directory-Titel erzeugen und schreiben
p = &buf[8];
for (i=0; i<16 && fib.fib_FileName[i]; i++)
*p++ = conv_to_64(fib.fib_FileName[i]);
Write(fh, buf, 32);
// Für jeden Verzeichniseintrag eine Zeile erzeugen und schreiben
while (ExNext(drive->lock, &fib)) {
// Zeile mit Leerzeichen löschen und mit Nullbyte abschließen
memset(buf, ' ', 31);
buf[31] = 0;
p = buf;
*p++ = 0x01; // Dummy-Verkettung
*p++ = 0x01;
// Größe in Blocks berechnen und eintragen
i = (fib.fib_Size + 254) / 254;
*p++ = i & 0xff;
*p++ = (i >> 8) & 0xff;
p++;
if (i < 10) p++; // Kleiner als 10: Ein Leerzeichen dazunehmen
if (i < 100) p++; // Kleiner als 100: Noch ein Leerzeichen dazunehmen
// Dateiname wandeln und eintragen
*p++ = '\"';
q = p;
for (i=0; i<16 && fib.fib_FileName[i]; i++)
if (fib.fib_FileName[i])
*q++ = conv_to_64(fib.fib_FileName[i]);
*q++ = '\"';
p += 18;
// Typ ermitteln und eintragen
if (fib.fib_DirEntryType >= 0) {
*p++ = 'D';
*p++ = 'I';
*p++ = 'R';
} else if (fib.fib_Protection & FIBF_EXECUTE) {
*p++ = 'S';
*p++ = 'E';
*p++ = 'Q';
} else {
*p++ = 'P';
*p++ = 'R';
*p++ = 'G';
}
// Datei geschützt?
if (fib.fib_Protection & FIBF_DELETE) *p++ = '<';
// Zeile schreiben
Write(fh, buf, 32);
}
// Abschlußzeile
Write(fh, "\001\001\0\0BLOCKS FREE. \0\0", 32);
Close(fh);
// Datei zum Lesen öffnen und das erste Zeichen lesen
if (drive->handle[channel] = Open("T:Frodo$File", MODE_OLDFILE))
drive->read_char = FGetC(drive->handle[channel]);
return ST_OK;
}
/**
** Kanal schließen
**/
int FS_Close(DriveData *drive, int channel)
{
if (channel==15) {
close_all_channels(drive);
return ST_OK;
}
if (drive->handle[channel]) {
Close(drive->handle[channel]);
drive->handle[channel] = NULL;
}
return ST_OK;
}
/*
* Alle Kanäle schließen
*/
void close_all_channels(DriveData *drive)
{
int i;
for (i=0; i<15; i++) FS_Close(drive, i);
drive->cmd_length = 0;
}
/**
** Ein Byte aus Kanal lesen
**/
int FS_Read(DriveData *drive, int channel, char *data)
{
LONG c;
// Kanal 15: Fehlerkanal
if (channel == 15) {
*data = *drive->error_ptr++;
if (*data != '\r')
return ST_OK;
else {
SetError(drive, ERR_OK);
return ST_EOF;
}
}
if (!drive->handle[channel]) return ST_READ_TIMEOUT;
// Zeichen aus dem Puffer holen und nächstes Zeichen lesen
*data = drive->read_char;
c = FGetC(drive->handle[channel]);
drive->read_char = c;
if (c == -1)
return ST_EOF;
else
return ST_OK;
}
/**
** Ein Byte in Kanal schreiben
**/
int FS_Write(DriveData *drive, int channel, char data, char eof)
{
// Kanal 15: Zeichen sammeln und bei EOF Befehl ausführen
if (channel == 15) {
if (drive->cmd_length >= 40)
return ST_TIMEOUT;
drive->cmd_buffer[drive->cmd_length++] = data;
if (eof < 0) {
drive->cmd_buffer[drive->cmd_length++] = 0;
drive->cmd_length = 0;
execute_command(drive, drive->cmd_buffer);
}
return ST_OK;
}
if (!drive->handle[channel]) {
SetError(drive, ERR_FILENOTOPEN);
return ST_TIMEOUT;
}
if (FPutC(drive->handle[channel], (unsigned char)data) < 0) {
SetError(drive, ERR_WRITEERROR);
return ST_TIMEOUT;
}
return ST_OK;
}
/*
* Befehlsstring ausführen
*/
void execute_command(DriveData *drive, const char *command)
{
APTR args;
switch (command[0]) {
case 'B':
args = "B-?";
if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
ResetC64();
SetError(drive, ERR_SYNTAX30);
break;
case 'M':
args = "M-?";
if (ShowRequester(MSG_UNSUPFLOPPYCMD, MSG_REQGADS3, &args))
ResetC64();
SetError(drive, ERR_SYNTAX30);
break;
case 'I':
close_all_channels(drive);
SetError(drive, ERR_OK);
break;
case 'U':
if ((command[1] & 0x0f) == 0x0a) {
close_all_channels(drive);
SetError(drive, ERR_STARTUP);
} else
SetError(drive, ERR_SYNTAX30);
break;
default:
SetError(drive, ERR_SYNTAX30);
break;
}
}
/*
* Umwandlung PETSCII->ASCII
*/
char conv_from_64(char c)
{
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
return c ^ 0x20;
if ((c == '/') && MapSlash)
return '\\';
return c;
}
/*
* Umwandlung ASCII->PETSCII
*/
char conv_to_64(char c)
{
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
return c ^ 0x20;
if ((c == '\\') && MapSlash)
return '/';
return c;
}